/*
 * Copyright 2011 Casey Stehlik, Brad, Tyler
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *              http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.softeng.network.chess;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.ObjectInputStream;
import java.util.Random;

public class Chess extends Client implements MouseListener
{
    //private static final Component MouseListener = null; 
	/*
	 * OPTIONS:
	 * 		- TREAT THE CHESSBOARD AS AN ARRAY.  UPDATE ARRAY(ROW, COL) EVERY TIME A MOVE IS MADE
	 * 		  EVERY TIME A MOVE IS MADE THE BOARD ARRAY IS UPDATED.  ONCE MOUSE IS CLICKED ARRAY WILL 
	 *        BE SEARCHED.  ONCE MOUSE IS CLICKED AGAIN THE ARRAY WILL BE SEARCHED AGAIN AND THE BOARD 
	 *        ARRAY WILL BE UPDATED APPROPRIATELY*/
	
		public String myColor = null;
		
		public String oppColor = null;
		
		public String currPlayer = "black";
		
		public JLabel lblCurrPlayer = null;
		
		public JFrame mWindow;
		
        public String mGameTitle = "Network Chess";
        
        public int mWidth = 600;
        
        private static Chess instance;
                
        public int mHeight = 600;
        
        public JMenuBar mMenuBar;
        
        public int currentPosition = -1; 
        
        public boolean firstClick = false; 
        
        /*
         * this array will track the state of each square on the board.  
         * Each square will hold that unique identifier for each chess piece 
        */
        public ChessPiece[] boardArray;  
        
        //this array will track the state of each square on the board and will not allow moving your pieces
        //results in the "check" of your own king
        public ChessPiece[] checkArray;
        
        public boolean isServer = false;
        
      //-----------------------------------------------------------------------------------------------
        
		private Chess( boolean isServer )
        {				
			this.isServer = isServer;
				if(isServer)
				{
					initTeamColor();
					NetworkCommand cmd = new NetworkCommand( this.getClientID() );
					cmd.setType( NetworkCommand.INITIALIZE );
					cmd.setColor(this.myColor);
					this.sendMessage( cmd );
				}
				else
				{
					initTeamColor();
					while( myColor == null)
					{
						
					}
				}
				
				initBoardArray(); 
								
                this.mWindow = new JFrame(this.mGameTitle);             

                //label that displays whose turn it is
                lblCurrPlayer = new JLabel();
                lblCurrPlayer.setText("");
                lblCurrPlayer.setOpaque(true);
                
                this.initBoard();
                this.initPieces();            
                
                this.mWindow.setSize(new Dimension(this.mWidth ,this.mHeight));
                
                this.mMenuBar = new JMenuBar();
                
                //--------------------------------------------------------------------------------------
                //File menu and its dropdown options
                JMenu fileMenu = new JMenu("File");

                this.mMenuBar.add(fileMenu);

                JMenuItem saveGameMenu = new JMenuItem("Save Game");
                fileMenu.add(saveGameMenu);
                KeyStroke ctrlSKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_S, InputEvent.CTRL_MASK);
                saveGameMenu.setAccelerator(ctrlSKeyStroke);
                saveGameMenu.addActionListener(new ActionListener()
                {
                	@Override
        			public void actionPerformed(ActionEvent e)
                	{
						JOptionPane.showMessageDialog(null, "Your game has been saved.");
        			}
                });

                JMenuItem loadGameMenu = new JMenuItem("Load Game");
                fileMenu.add(loadGameMenu);
                KeyStroke ctrlLKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_L, InputEvent.CTRL_MASK);
                loadGameMenu.setAccelerator(ctrlLKeyStroke);
                loadGameMenu.addActionListener(new ActionListener()
                {
                	@Override
        			public void actionPerformed(ActionEvent e)
                	{
						JOptionPane.showMessageDialog(null, "Your saved game has been loaded.");
                	}
                });

                JMenuItem requestDraw = new JMenuItem("Request Draw");
                fileMenu.add(requestDraw);
                KeyStroke ctrlDKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_D, InputEvent.CTRL_MASK);
                requestDraw.setAccelerator(ctrlDKeyStroke);
                requestDraw.addActionListener(new ActionListener()
                {
                	@Override
                	public void actionPerformed(ActionEvent e)
                	{
                		boolean d = false;
                		boolean a = false;
                		String draw;
                		String accept;
                		if (currPlayer.equals(myColor))
                		{
                			while (!d)
                			{
                				draw = JOptionPane.showInputDialog("Are you sure you want to request a draw? Y/N");

                				//not sure if we can actually finish this part because we don't have a network
                				if (draw.equalsIgnoreCase("Y") && currPlayer.equals(oppColor)){ //can't test this because I don't have a network to run it with
                					while (!a)
                					{
	                					accept = JOptionPane.showInputDialog("The " + currPlayer + " piece player requests a draw.  Do you accept?  Y/N");

	                					//If your opponent accepts your draw offer
	                					if (accept.equalsIgnoreCase("Y"))
	                					{
	                						JOptionPane.showMessageDialog(null, "The match is a draw.");
	                						a = true;
	                					}

	                					//If you decline the draw offer
	                					else if (accept.equalsIgnoreCase("N") && currPlayer.equals(myColor))
	                					{
	                						JOptionPane.showMessageDialog(null, "You declined their draw request.  You win!!");
	                						a = true;
	                					}
	                					//If you accept you opponent's draw offer
	                					else if (accept.equalsIgnoreCase("Y") && currPlayer.equals(oppColor))
	                					{
	                						JOptionPane.showMessageDialog(null, "You accepted their draw request.  The match is a draw.");
	                						a = true;
	                					}
                					}	                		
                					mWindow.setVisible(false);
                					d = true;
                				}
                				else if (draw.equalsIgnoreCase("N"))
                				{
                					d = true;                					
                				}
                			}
                		}
                	}
                });

                JMenuItem forfeit = new JMenuItem("Forfeit");
                fileMenu.add(forfeit);
                KeyStroke ctrlFKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_F, InputEvent.CTRL_MASK);
                forfeit.setAccelerator(ctrlFKeyStroke);
                forfeit.addActionListener(new ActionListener()
                {
                	@Override
        			public void actionPerformed(ActionEvent e)
                	{
                		boolean f = false;
                		String forf;
                		while (!f)
                		{
	                		forf = JOptionPane.showInputDialog("Are you sure you want to forfeit? Y/N");
	                		//If the other player forfeits
	                		if (forf.equalsIgnoreCase("Y") && !currPlayer.equals(myColor))
	                		{ 
	                			JOptionPane.showMessageDialog(null, "The " + currPlayer + " piece player forfeits.  You win!");
	    						Chess.getInstance(false).mWindow.setVisible(false);
	    						f = true;
	                		}
	                		//If you forfeit
	                		else if (forf.equalsIgnoreCase("Y") && currPlayer.equals(myColor))
	                		{
	                			JOptionPane.showMessageDialog(null, "You forfeit.  You lose the match!");
	                			f = true;
	                		}
	                		//If you don't want to forfeit
	                		else if (forf.equalsIgnoreCase("N"))
	                		{
	                			f = true;
	                		}
                		}
        			}
                });

                //-------------------------------------------------------------------------------------
                //Edit menu and its dropdown options
                JMenu editMenu = new JMenu("Edit");
                this.mMenuBar.add(editMenu);
                JMenuItem undoMoveMenu = new JMenuItem("Undo Move");
                editMenu.add(undoMoveMenu);
                KeyStroke ctrlZKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_Z, InputEvent.CTRL_MASK);
                undoMoveMenu.setAccelerator(ctrlZKeyStroke);
                
                //-------------------------------------------------------------------------------------
                //Settings menu and its dropdown options
                JMenu settingsMenu = new JMenu("Help");
                this.mMenuBar.add(settingsMenu);
                JMenuItem chessRulesMenu = new JMenuItem("How to use Network Chess");
                settingsMenu.add(chessRulesMenu);
                KeyStroke ctrlHKeyStroke = KeyStroke.getKeyStroke(KeyEvent.VK_H, InputEvent.CTRL_MASK);
                chessRulesMenu.setAccelerator(ctrlHKeyStroke);
                chessRulesMenu.addActionListener(new ActionListener()
                {
                	@Override
        			public void actionPerformed(ActionEvent e)
                	{
                		HelpingMenu help = new HelpingMenu(); 
                		help.display();  
        			}
                });

                //-------------------------------------------------------------------------------------
                //Label to create space between menu options and the player color turn display
                JLabel blank = new JLabel("                                      ");
                mMenuBar.add(blank);
                mMenuBar.add(lblCurrPlayer);               
                this.mWindow.setJMenuBar(this.mMenuBar);
                this.mWindow.setResizable(false);
                mWindow.addMouseListener(this);
                this.mWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                switchPlayer();
        }
		
		// randomly generates which color you are
		public void initTeamColor()
		{
			Random randomGenerator = new Random();
			int randomInt = randomGenerator.nextInt(2);
			if (randomInt == 0)
			{
				myColor = "black";
				oppColor = "white";
			} 
			else
			{
				myColor = "white";
				oppColor = "black";
			}			

			JOptionPane.showMessageDialog(null, "Your pieces will be " + myColor + " for this game.");
		}

		//Switches to allow the other person to move
		public void switchPlayer()
		{
			if(currPlayer == "white")
            {
                currPlayer = "black";
                lblCurrPlayer.setText("Black Player's Turn");
                lblCurrPlayer.setBackground(Color.BLACK);
                lblCurrPlayer.setForeground(Color.WHITE);
            }
            else if(currPlayer == "black" || currPlayer == " ")
            {
                currPlayer = "white";
                lblCurrPlayer.setText("White Player's Turn");
                lblCurrPlayer.setBackground(Color.WHITE);
                lblCurrPlayer.setForeground(Color.BLACK);
            }
        }
		
		//-----------------------------------------------------------------------------------------------
		
		public void initBoardArray(){ 
			
			boardArray = new ChessPiece[64];
			
			for (int n = 0; n < 64; n++){
				
				boardArray[n] = null; 
				
			}
			
			checkArray = boardArray;
			
		}
		
		//-----------------------------------------------------------------------------------------------
        
        public void initBoard(){
                
        	JPanel panel = new JPanel();
            panel.setLayout(new GridLayout(8, 8));
                  
            for(int x = 0; x < 64; x++){
                        
            	JPanel square = new JPanel();
                square.setLayout(new GridLayout());
                        
                int row = (x / 8) % 2;
			    if (row == 0)	                
			    
			    	square.setBackground( x % 2 == 0 ? new Color(0, 120, 22) : new Color(250, 242, 197) );
			            
			    else
			        square.setBackground( x % 2 == 0 ? new Color(250, 242, 197) : new Color(0, 120, 22) );
			                        
                        panel.add(square);
                        
                }
                
                this.mWindow.setContentPane(panel);
                
        }
        
      //-----------------------------------------------------------------------------------------------
        
        public void initPieces(){
        	
        	//initialize black pieces   	
        	Rook r1B = new Rook("black",0,mWindow); 
        	boardArray[0] = r1B;     	   	
        	Knight k1B = new Knight("black",1,mWindow);
        	boardArray[1] = k1B;
        	Bishop b1B = new Bishop("black",2,mWindow);
        	boardArray[2] = b1B;
        	
        	if (oppColor.equals("black"))
        	{
	        	Queen qB = new Queen(oppColor,3,mWindow);
	        	boardArray[3] = qB;
	        	King kB = new King(oppColor,4,mWindow);
	        	boardArray[4] = kB; 
        	}
        	else
        	{
        		King kB = new King(oppColor,3,mWindow);
	        	boardArray[3] = kB;
	        	Queen qB = new Queen(oppColor,4,mWindow);
	        	boardArray[4] = qB;
        	}
        	
        	Bishop b2B = new Bishop("black",5,mWindow);
        	boardArray[5] = b2B;
        	Knight k2B = new Knight("black",6,mWindow);
        	boardArray[6] = k2B;
        	Rook r2B = new Rook("black",7,mWindow);
        	boardArray[7] = r2B;
        	Pawn p1B = new Pawn("black",8,mWindow);
        	boardArray[8] = p1B;
        	Pawn p2B = new Pawn("black",9,mWindow);
        	boardArray[9] = p2B;
        	Pawn p3B = new Pawn("black",10,mWindow);
        	boardArray[10] = p3B;
        	Pawn p4B = new Pawn("black",11,mWindow);
        	boardArray[11] = p4B;
        	Pawn p5B = new Pawn("black",12,mWindow);
        	boardArray[12] = p5B;
        	Pawn p6B = new Pawn("black",13,mWindow);
        	boardArray[13] = p6B;
        	Pawn p7B = new Pawn("black",14,mWindow);
        	boardArray[14] = p7B;
        	Pawn p8B = new Pawn("black",15,mWindow);
        	boardArray[15] = p8B;
        	
        	//initialize white pieces
        	Pawn p1W = new Pawn("white",48,mWindow);
        	boardArray[48] = p1W;
        	Pawn p2W = new Pawn("white",49,mWindow);
        	boardArray[49] = p2W;
        	Pawn p3W = new Pawn("white",50,mWindow);
        	boardArray[50] = p3W;
        	Pawn p4W = new Pawn("white",51,mWindow);
        	boardArray[51] = p4W;
        	Pawn p5W = new Pawn("white",52,mWindow);
        	boardArray[52] = p5W;
        	Pawn p6W = new Pawn("white",53,mWindow);
        	boardArray[53] = p6W;
        	Pawn p7W = new Pawn("white",54,mWindow);
        	boardArray[54] = p7W;
        	Pawn p8W = new Pawn("white",55,mWindow);
        	boardArray[55] = p8W;
        	Rook r1W = new Rook("white",56,mWindow);
        	boardArray[56] = r1W;
        	Knight k1W = new Knight("white",57,mWindow);
        	boardArray[57] = k1W;
        	Bishop b1W = new Bishop("white",58,mWindow);
        	boardArray[58] = b1W;
        	
        	if (myColor.equals("white"))
        	{
	        	Queen qW = new Queen(myColor,59,mWindow);
	        	boardArray[59] = qW;
	        	King kW = new King(myColor,60,mWindow);
	        	boardArray[60] = kW;
        	}
        	else
        	{
        		King kW = new King(myColor,59,mWindow);
	        	boardArray[59] = kW;
	        	Queen qW = new Queen(myColor,60,mWindow);
	        	boardArray[60] = qW;
        	}
        	
        	Bishop b2W = new Bishop("white",61,mWindow);
        	boardArray[61] = b2W;
        	Knight k2W = new Knight("white",62,mWindow);
        	boardArray[62] = k2W;
        	Rook r2W = new Rook("white",63,mWindow);
        	boardArray[63] = r2W;
        	
        	checkArray = boardArray;
            checkArray = boardArray;
        }
        
      //-----------------------------------------------------------------------------------------------
        
        
        public void display(){
        	
                this.mWindow.setVisible(true);
                
        }
        

      //-----------------------------------------------------------------------------------------------
        
        public int getRow(int posY){
        	
        	int row = 0;
        	//Y axis starts at posY = 46.  this value needs to be subtracted from
        	//mWidth to calculate the row
        	
        	if (posY > 0+46 && posY <= (((mWidth-46)/8)+46)){
        		row = 8; 
        	}
        	else if (posY > (((mWidth-46)/8)+46) && posY <= (2*(((mWidth-46)/8))+46)){
        		row = 7; 
        	}
        	else if (posY > (2*(((mWidth-46)/8))+46) && posY <= (3*(((mWidth-46)/8))+46)){
        		row = 6; 
        	}
        	else if (posY > (3*(((mWidth-46)/8))+46) && posY <= (4*(((mWidth-46)/8))+46)){
        		row = 5; 
        	}
        	else if (posY > (4*(((mWidth-46)/8))+46) && posY <= (5*(((mWidth-46)/8))+46)){
        		row = 4; 
        	}
        	else if (posY > (5*(((mWidth-46)/8))+46) && posY <= (6*(((mWidth-46)/8))+46)){
        		row = 3; 
        	}
        	else if (posY > (6*(((mWidth-46)/8))+46) && posY <= (7*(((mWidth-46)/8))+46)){
        		row = 2; 
        	}
        	else if (posY > (7*(((mWidth-46)/8))+46) && posY <= (8*(((mWidth-46)/8))+46)){
        		row = 1; 
        	}
        	
        	return row; 
        	
        }
        
      //-----------------------------------------------------------------------------------------------
        
        public int getCol(int posX){
        	
        	int col = 0;
        	
        	if (posX > 0 && posX <= mHeight/8){
        		col = 1; 
        	}
        	else if (posX > mHeight/8 && posX <= 2*mHeight/8){
        		col = 2; 
        	}
        	else if (posX > 2*mHeight/8 && posX <= 3*mHeight/8){
        		col = 3; 
        	}
        	else if (posX > 3*mHeight/8 && posX <= 4*mHeight/8){
        		col = 4; 
        	}
        	else if (posX > 4*mHeight/8 && posX <= 5*mHeight/8){
        		col = 5; 
        	}
        	else if (posX > 5*mHeight/8 && posX <= 6*mHeight/8){
        		col = 6; 
        	}
        	else if (posX > 6*mHeight/8 && posX <= 7*mHeight/8){
        		col = 7; 
        	}
        	else if (posX > 7*mHeight/8 && posX <= 8*mHeight/8){
        		col = 8; 
        	}
        	
        	return col; 
        	
        }

//-----------------------------------------------------------------------------------------------

//takes a row and column set and returns the corresponding square position on the board

	public int rowCol2Position(int row, int col){
		
		int position = 0;
		
		int count = 0;
		
		int m,n; 
		
		for (n = 1; n < 9; n++){
			
			for (m = 1; m < 9; m++){
				
				if(row == (9-n) && col == m){
					
					position = count;
					
				}
				else{
					count += 1; 
				}
			}
		}
		
		return position;
	}

	public static Chess getInstance( boolean isServer ){
		
		if (instance == null){
			instance = new Chess( isServer );
		}
		
		return instance;
	
	}
	
	
	
	
	//-----------------------------------------------------------------------------------------------
	//--------------------------------mouse events---------------------------------------------------
	//-----------------------------------------------------------------------------------------------
	
	@Override
	public void mouseClicked(MouseEvent e) 
	{
		
		boolean finishedMove = false; 
		int row = getRow(e.getY());
		int col = getCol(e.getX());
		int position = rowCol2Position(row,col);
		
		if(currentPosition != -1 && firstClick == true)
		{
			if(boardArray[position] == null || boardArray[position].color != currPlayer)
			{
				boardArray[currentPosition].commitMove(position, mWindow, currentPosition, boardArray);		
				
				NetworkCommand cmd = new NetworkCommand( this.getClientID() );
				cmd.setType( NetworkCommand.MOVE );
				cmd.setCurPosition( currentPosition );
				cmd.setPosition( position );
				
				this.sendMessage( cmd );
				
				firstClick = false; 	
				finishedMove = true; 
				currentPosition = -1; 
				
				checkArray = boardArray;
	            checkArray = boardArray;
			}
		}
			
		if(boardArray[position] != null && finishedMove == false && boardArray[position].color.equals(currPlayer)){
			currentPosition = position;
			firstClick = true;
			System.out.println("you selected a " + boardArray[position].color + " " + boardArray[position].idString +"!!!");
			
		}		
	}
	
	@Override
	public void mouseEntered(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	@Override
	public void mouseExited(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	@Override
	public void mousePressed(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	@Override
	public void mouseReleased(MouseEvent arg0) {
		// TODO Auto-generated method stub
		
	}
	
	@Override
	public void run()
	{
		Object obj = null;
		
		while( this.connected )
		{
			//System.out.println( "[CLIENT] - Client is listening for response from server..." );
			try
			{
				ObjectInputStream ois = new ObjectInputStream(conn.getInputStream());
				obj = ois.readObject();
				System.out.println( "[CLIENT] - Recieved Message: " + (String)obj );
				
				NetworkCommand cmd = new NetworkCommand();
				cmd.parse( (String)obj );
				
				if( cmd.getType().equals( "MOVE" ) )
				{
					System.out.println("Cur: " + cmd.getCurPosition() + "Pos: " + cmd.getPosition());
					boardArray[cmd.getCurPosition()].commitMove(cmd.getPosition(), mWindow, cmd.getCurPosition(), boardArray);
				}
				
				if( cmd.getType().equals( "INIT" ) )
				{
					System.out.println();
					this.oppColor = cmd.getColor();
					
					if( this.oppColor.equals("white") )
					{
						myColor = "black";
					}
					else
					{
						myColor = "white";
					}
				}
			} 
			catch (Exception e)
			{
				if( obj != null)
				{
					System.out.println( obj );
					
				}
				connected = false;
				System.out.println( "[CLIENT] - Client has quit" );
				e.printStackTrace();
			}
		}
	}
}
